/*
 * @QueueDumper.java 2008/07/28
 *
 * Copyright 2008 Web & Zen Multimedia, All rights reserved.
 * Use is subject to license terms.
 */
package it.wzm.lightweightlog;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Properties;


/**
 * This class will retrieve the Entry from the Queue and dispatch to Register
 */
final class QueueDumper implements Runnable, Constant {

	/**
	 * The interval before we dump
	 */
	private long dumpInterval;
	/**
	 * Our queue
	 */
    private Queue<? extends Entry> q;
    /**
     * Our register
     */
    private Register<? extends Dumper> r;
    
    enum Status {
    	INIT,
    	RUNNING,
    	DUMPING,
    	SLEEPING,
    	SHUTDOWN,
    	FAILURE,
    }
    /**
     * Current status
     */
    private Status status;
	/**
	 * Properties
	 */
    private Properties p;
    /**
     * Log
     */
	private Log logger;
        

	/**
     * Build a Queue Dumper 
     * @param p Custom set of property
     */
    public QueueDumper(Properties p) {
        assert (p != null) : "Property null";
        this.init(p);
        status=Status.RUNNING;
    }
    
    /**
     * Getter for status
     * @return status
     */
    public Status getStatus() {
		return status;
	}

    /**
     * Initialize our Queue dumper.
     */
    private void init(Properties p) {
    	// store properties...
    	this.p=p;
    	logger = LogFactory.getLog();
    	
    	try {
	        // looking for dump interval
    		String di = this.getProperty("dumpinterval");
	        if (di!=null) {
	            this.dumpInterval = Long.parseLong(di);
	
	            // however by default we cannot dump less then 1 second
	            if (this.dumpInterval < 1000)
	                this.dumpInterval = DEFAULT_DUMP_INTERVAL;
	        } else {
	            this.dumpInterval = DEFAULT_DUMP_INTERVAL;
	        }
	        
    	} catch (Exception e) {
    		throw new QueueDumperInitializationException(e);
    	}
    }
    
    /**
     * Get property from our property set... 
     */
    protected String getProperty(String name) {
        for (Object key: p.keySet()) {
            if (((String)key).indexOf(Constant.PROP_ROOT + "queuedumper." + name) > -1) {
                return (String) p.get(key);
            }
        }
        // nothing found...
        return null;
    }
    
    /**
     * Set current status
     * @param status
     */
    private void setStatus(Status status) {
    	//System.out.println(status);
    	if (this.status==Status.SHUTDOWN)
    		return;
    	else 
    		this.status=status;
    }
    
    /**
     * Implementing the run() method 
     */
    public void run() {
    	
        // these are needed by our QueueDumper        
        q = LogFactory.getQueue();
        r = LogFactory.getRegister();    

		// we cannot start a QueueDumper when register does not contain Dumper
        // or the Queue is not cumulable, this check is done by the LogFactory first
        // but another check is made now...
        if (!q.isCumulable() || r.size() < 1) {
            return;
        }
        
        try {
            // just a labeled loop
            dumping:
            while (status != Status.SHUTDOWN) {
            	// let's start...
            	this.setStatus(Status.DUMPING);
            	
                // get queue and a list of Entry
            	LinkedList<? extends Entry> l;
            	synchronized(this) { 
            		l = q.retainAll();
            	};
            	
                // basic check if our queue
                // does contain any Entry...
                if (!l.isEmpty()) {
                    r.dispatchToDumper(l);
                    // clear entry from the list...
                    l.clear();
                    l=null;
                }
                // just pausing a while...before retrying
                this.setStatus(Status.SLEEPING);
                Thread.sleep(dumpInterval);
            }

        } catch (InterruptedException e) {
            //LogFactory.getLog().fatal(e.getMessage());
        	logger.fatal(e);
            this.status=Status.FAILURE;
        } finally {
        	// now it's time to free all resources
        	// just ask to ObjectFactory...
        	//LogFactory.shutdown();
        }
    }

    /**
     * Shutdown QueueDumper
     */
	void shutdown() {
		
		shutdown:
		while (true) {
			
			try {
				if (this.status==Status.SLEEPING) {
					// set shutdown status...
					this.setStatus(Status.SHUTDOWN);
					
	            	ThreadGroup g = Thread.currentThread().getThreadGroup();            	
	            	Thread[] T = new Thread[g.activeCount()];
	            	g.enumerate(T, false);
	            	
	            	for (Thread x: Arrays.asList(T)) {
	            		// if it's found...we break the loop
	            		// before we interrupt the thread...
	            		if (x.getName().equals("QueueDumper")) {
	            			x.interrupt();
	            			break shutdown;
	            		}
	            	}			
				} else {
					Thread.sleep(1000);
				}
			} catch (InterruptedException e) {
				break shutdown;
			}
		}
	}    
	
	/**
	 * Override inherithed method
	 */
	@Override
	public String toString() {
		
		StringBuffer sb = new StringBuffer();
		
        for (Object key: p.keySet()) {
            if (((String)key).indexOf(Constant.PROP_ROOT + "queuedumper." ) > -1) {
                sb.append(  "," + ((String)key).replaceAll(Constant.PROP_ROOT + "queuedumper.", "") + "=" +  p.get(key) );
            }
        }
        
        //  returning...
		return super.toString() + " {hc=" + this.hashCode() + "status=" + this.status + sb + "}" ;
	}

	/**
	 * Current dump interval
	 * @return long
	 */
	public long getDumpInterval() {
		return this.dumpInterval;
	}
}	
	
	

